//
//
//
//
//
//
// TCPServer.C

#include "SocketHandleBase.h"
#include "LogManager.h"
#include "Exception.h"
#include "TimeVal.h"
#include "StrStream.h"
#include "TCPServer.h"

TCPServer::TCPServer(const NetAddr& addr) : 
	_bindAddr(addr), _sockStatus(TCPServer::SOCK_INVALIDE)
{
	if(!_bindAddr.Valide())
	{
		Util::StrStream str("Invalide address,");
		str << _bindAddr.AsString();
		throw Exception(str.AsString());
	}
	_sockStatus = TCPServer::SOCK_INITIALED;
}

TCPServer::~TCPServer()
{
	;
}

int TCPServer::HandleEvents(const TimeVal& time)
{
	// means, this function was firstly called
	// need do the following 
	// 1, bind & listen
	// 2, clr rd/wt/exp sets
	// 3, add server sock to rd sets
	if(_sockStatus < SOCK_LISTEN)
	{
		_serverSock.Bind(_bindAddr);
		_serverSock.Listen(5);
		_sockStatus = SOCK_LISTEN;

		S32 fileID = _serverSock.FileID();

		FD_ZERO(&_rdSet);
		FD_SET(fileID, &_rdSet);
	}

	// event loop

	struct timeval val;
	val.tv_sec = 5;
	val.tv_usec = 0;
	S32 ret = 0;
	for(;;)
	{
		FD_SET(_serverSock.FileID(), &_rdSet);
		ret = select(GetMaxFD() + 1,
				&_rdSet,
				NULL,
				NULL,
				&val);
		if(0 == ret)
		{
			LOGINFO("HandleEvents select timeout");
			continue;
		}
		if(-1 == ret)
			break;

		// handle listen socket event
		if(FD_ISSET(_serverSock.FileID(), &_rdSet))
		{
			try
			{
				NetAddr peerAddr;
				TCPSocket peerSock = _serverSock.Accept(peerAddr);
				_children[peerSock.FileID()] = peerSock;
				FD_SET(peerSock.FileID(), &_rdSet);
			}
			catch(const Exception& exp)
			{
				LOGERROR(exp.What());
			}
			continue;
		}
		std::map<S32, TCPSocket>::iterator ait = _children.begin();
		for(std::map<S32, TCPSocket>::iterator it = _children.begin();
				it != _children.end();)
		{
			ait = it;
			ait++;
			if(FD_ISSET(it->first, &_rdSet))
			{
				RouteMsg(it->second, Socket::_READ_EVENT);
			}
			it = ait;
		}
	}
	if(-1 == ret)
	{
		Util::StrStream str;
		str << "Error HandleEvents : "
			<< strerror(errno);
		throw Exception(str.AsString());
	}

	// now add the listen socket to the fd sets
	return 0;
}

void TCPServer::RouteMsg(TCPSocket& sock, Socket::EVENTS_OPT event)
{
	try
	{
		if(event & _evnMask 
				&& event & Socket::_READ_EVENT)
		{
			S32 ret = 0;
			ret = _evnHandler->HandleInput(sock);
			if(ret == 0)
			{
				FD_CLR(sock.FileID(), &_rdSet);
				RemoveSocket(sock);
			}
		}
	}
	catch(const Exception& exp)
	{
		// socket error
		LOGERROR(exp.What());
	}
	catch(...)
	{
		LOGERROR("UNKNOW exception...");
	}
}


void TCPServer::RegisterHandler(SocketHandleBase* wor, Socket::EVENTS_OPT mask)
{
	if(NULL == wor)
		throw Exception("NULL parameter entered.");
	if(mask <= Socket::_NONE_EVENT || mask > Socket::_ALL_EVENT)
	{
		Util::StrStream str;
		str << "Invalide mask : "
			<< mask;
		throw Exception(str.AsString());
	}
	_evnMask = mask;
	_evnHandler = wor;
}


// Remove children socket from map when client closed socket
//
void TCPServer::RemoveSocket(TCPSocket& sock)
{
	_children.erase(sock.FileID());
}

// Get maxFD from children map, it will return listen socket.fileId when no connection 
// created.
U32 TCPServer::GetMaxFD(void) const
{
	U32 maxFileID = _serverSock.FileID();
	std::map<S32, TCPSocket>::const_iterator it = _children.begin();
	for(; it != _children.end(); ++it)
	{
		if(it->first > maxFileID)
			maxFileID = it->first;
	}
	return maxFileID;
}
